import time
import json
import os
import isotp
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.connections import QueueConnection
from udsoncan.client import Client
from udsoncan import Routine
from gutil import EcuFlashFile
from gutil import UtilStr
from udsoncan.services import RequestDownload
from udsoncan.services import TransferData
from binascii import crc32

import udsoncan.configs
import logging
from threading import Thread
from queue import Queue
import traceback
# logger_class = Logger()
# logger = logger_class.GetLogger()

class MyCustomCodecThatShiftBy4(udsoncan.DidCodec):
   def encode(self, val):
      val = (val << 4) & 0xFFFFFFFF # Do some stuff
      return struct.pack('<L', val) # Little endian, 32 bit value

   def decode(self, payload):
      val = struct.unpack('<L', payload)[0]  # decode the 32 bits value
      return val >> 4                        # Do some stuff (reversed)

   def __len__(self):
      return 4    # encoded paylaod is 4 byte long.



class EcuDiag:
    """
    开启一个诊断事件线程
    示例流程:
        实例化
        设置can
        设置ECU_select_path
        调用诊断命令
    """

    Coompany_array = []
    Vehicle_array = []
    Ecu_array = []
    Config_dict = {}  # 所有 ecuconfig 目录下的json配置文件信息总和
    Ecu_config_dict = {}
    Config_rout_file_array = []
    Uds_client = Client(None)  # 后面必须被覆盖掉， 此处只是为了解决COC的语法问题
    Uds_client_dict = {}  # {控制器:uds_client}
    Transfer_data_max_len = 0
    # Can_channel = None
    Conn = QueueConnection()
    Tx_queue = Conn.touserqueue
    Rx_queue = Conn.fromuserqueue
    Diag_event_quee = Queue(maxsize = 500)

    Last_uds_client = None

    EraseAllType = 0# 擦除方式， 0 分段擦除， 1 全擦除ff00
    def __init__(self):
        self.Config_dict = self.UpdateConfigDir({},"./EcuConfig" ,0)

        info = "获取诊断配置文件"
        logging.info(info+str(self.Config_dict))

        diag_thread = Thread( target=self.DiagLoop)  # 通过当前线程开启新的线程去启动事件循环
        diag_thread.start()
        pass
    def DiagLoop(self):
        '''
        启动诊断线程
        '''
        logging.info("启动诊断线程")
        while(True):
            qlen = self.Diag_event_quee.qsize()
            if(qlen>0):
                event_list = self.Diag_event_quee.get()
                logging.debug("处理事件"+str([evt["命令"] for evt in event_list]))
                self.DiagComplexEvent(event_list)
            else:
                time.sleep(0.1)

    def SetCan(self, can):
        self.Can_channel = can
        info = "设置诊断CAN"
        print('Dbg:"EcuDiag.py" :48 info=',info,can)
        pass

    def GetUdsClientQuee(self):
        #获取UDS连接信息
        return self.Conn

    def SetEcu(self, ecu_select_path):
        info = "诊断选择ECU"
        print('Dbg:"EcuDiag.py" :41 info=', info, ecu_select_path)
        self.Ecu_config_dict = self.GetEcuConfig(ecu_select_path)

    def SetUdsClient(self, uds_client):
        self.Uds_client = uds_client

    def GetUdsClient(self):
        if not self.Uds_client:
            print("诊断服务还未打开")
        return self.Uds_client

    def OpenUdsClient(self):
        """
        bus: 打开的CAN

        采用队列打开

        返回:None
        配置好的并打开udsClient
        设置uds_client
        打开一个新的UdsClient
        """
        bus = self.Can_channel
        if(not bus):
            info = "请先打开CAN卡"
            print('Dbg:"EcuDiag.py" :80 info=',info)
        ecu_config = self.GetEcuConfig()
        # TODO 如果udsclient 已经打开过的 就不要重新打开？
        if(self.Last_uds_client):#切换ECU的时候必须关闭上一个Client， 否则新的Client诊断会收不到数据
            self.Last_uds_client.close()

        if ecu_config["name"] in self.Uds_client_dict:
            logging.info("udsclient已经打开了")
            self.Uds_client_dict[ecu_config["name"]].open()
            self.Last_uds_client = self.Uds_client_dict[ecu_config["name"]]
            return self.Uds_client_dict[ecu_config["name"]]

        isotp_params = {
            "stmin": 32,  # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9
            "blocksize": 8,  # Request the sender to send 8 consecutives frames before sending a new flow control message
            "wftmax": 0,  # Number of wait frame allowed before triggering an error
            "ll_data_length": 8,  # Link layer (CAN layer) works with 8 byte payload (CAN 2.0)
            "tx_padding": 0,  # Will pad all transmitted CAN messages with byte 0x00. None means no padding
            "rx_flowcontrol_timeout": 5000,  # Triggers a timeout if a flow control is awaited for more than 1000 milliseconds
            "rx_consecutive_frame_timeout": 5000,  # Triggers a timeout if a consecutive frame is awaited for more than 1000 milliseconds
            "squash_stmin_requirement": False,  # When sending, respect the stmin requirement of the receiver. If set to True, go as fast as possible.
        }

        # bus = VectorBus(channel=0, bitrate=500000)                                          # Link Layer (CAN protocol)
        txid=ecu_config["诊断ID"]["ECU收"],
        rxid=ecu_config["诊断ID"]["ECU发"],
        tp_addr = isotp.Address(
            isotp.AddressingMode.Normal_11bits,
            txid=ecu_config["诊断ID"]["ECU收"],
            rxid=ecu_config["诊断ID"]["ECU发"],
        )  # Network layer addressing scheme
        stack = isotp.CanStack(
            bus=bus, address=tp_addr, params=isotp_params
        )  # Network/Transport layer (IsoTP protocol)
        conn = PythonIsoTpConnection( stack)  # interface between Application and Transport layer

        logging.info("打开udsclient"+str(txid)+"rxid"+str(rxid))
        # conn = QueueConnection()
        client = Client(conn, request_timeout=5)

        client.config["exception_on_negative_response"] = False
        client.config["p2_timeout"] = 5
        client.config["request_timeout"] = 5
        client.config["use_server_timing"] = False
        client.config["security_algo"] = self.EcuAlgo
        client.config["security_algo_params"] = [
            0x4FE87269,
            0x6BC361D8,
            0x9B127D51,
            0x5BA41903,
        ]
        client.config["data_identifiers"] = {
            0xF1A8 : udsoncan.DidCodec('B'),
            0xF190 : udsoncan.DidCodec('BBBBBBBBBBBBBBBB'),
            0xF195 : udsoncan.DidCodec('B'),
            0xF199 : udsoncan.DidCodec('BBBBBBB')
        }
        client.config["server_address_format"] = 32
        client.config["server_memorysize_format"] = 32
        # print(client.config)
        # err
        client.open()
        self.Uds_client_dict = {ecu_config["name"]: client}
        info="打开UDSclient"
        print('Dbg:"EcuDiag.py" :137 info=',info)

        self.Last_uds_client = client
        # self.Uds_client = client
        return client

    def CloseUdsClient(self):
        """
        关闭所有打开的client
        """
        for client_key in self.Uds_client_dict.keys():
            if self.Uds_client_dict[client_key]:
                pass
                self.Uds_client_dict[client_key].close()

    def EcuAlgo(self, level, seed, params):
        temp_key_u32 = 0
        for tmp in seed:
            temp_key_u32 = (temp_key_u32 << 8) + tmp
            # TODO del
            # temp_key = (seed[0]<<24) | (seed[1] << 16) | (seed[2] << 8) | (seed[3])

        if level == 0x01:
            temp_key_u32 = (
                (((temp_key_u32 >> 4) ^ temp_key_u32) << 3) ^ temp_key_u32
            ) & 0xFFFFFFFF
        elif level == 0x11:
            temp_y = (
                ((temp_key_u32 << 24) & 0xFF000000)
                + ((temp_key_u32 << 8) & 0xFF0000)
                + ((temp_key_u32 >> 8) & 0xFF00)
                + ((temp_key_u32 >> 24) & 0xFF)
            )
            temp_z = 0
            temp_sum = 0
            for _ in range(64):
                temp_y = (
                    temp_y
                    + (
                        (((temp_z << 4) ^ (temp_z >> 5)) + temp_z)
                        ^ (temp_sum + params[temp_sum & 3])
                    )
                ) & 0xFFFFFFFF
                temp_sum = (temp_sum + 0x8F750A1D) & 0xFFFFFFFF
                temp_z = (
                    temp_z
                    + (
                        (((temp_y << 4) ^ (temp_y >> 5)) + temp_y)
                        ^ (temp_sum + params[(temp_sum >> 11) & 0x3])
                    )
                ) & 0xFFFFFFFF
            temp_key_u32 = (
                ((temp_z << 24) & 0xFF000000)
                | ((temp_z << 8) & 0xFF0000)
                | ((temp_z >> 8) & 0xFF00)
                | ((temp_z >> 24) & 0xFF)
            )
        else:
            temp_key_u32 = temp_key_u32

        # 将U32 数据切割成U8 数组
        output_key = bytes([(temp_key_u32 >> (3 - i) * 8) & 0xFF for i in range(4)])
        # logger.info("---------------------------")
        return output_key

    def DiagComplexEvent(self, event_list):
        # event 为UDS的基本命令
        # ["进扩展模式",读故障码,清故障码]
        # event_list为 为实现ui 的需求，由基本命令按时序组合
        # 诊断失败后，终止后续命令

        if self.Ecu_config_dict == None:
            info = "没有选择对应的控制器"
            logging.debug('info='+json.dumps(info,indent = 4))
        uds_client = self.OpenUdsClient()
        for event in event_list:
            if "info" not in event:
                event["info"] = "未定义"
            print(
                'Dbg:"EcuDiag.py" :165 event=',
                event["命令"],
                " 延时:",
                event["延时"],
                " 说明:",
                event["info"],
            )
            event_delay = event["延时"]
            diag_cmd = event["命令"]
            # diag_parmeter = event["参数"]
            res = False
            try:
                if diag_cmd == "进扩展模式":
                    response = uds_client.change_session(event["模式"])  # session 参数只有一个
                    res = self.DiagDisplayRespResult(response, "进扩展模式失败")
                    if not res: return
                elif diag_cmd == "Ecu复位":
                    response = uds_client.ecu_reset(0x11)  # session 参数只有一个
                    res = self.DiagDisplayRespResult(response, "复位失败")
                    if not res: return
                elif diag_cmd == "进安全模式":
                    response = uds_client.unlock_security_access(event["模式"])
                    if not self.DiagDisplayRespResult(response, "进安全模式"):
                        return
                elif diag_cmd == "读数据":
                    response = uds_client.read_data_by_identifier(event["DID"])
                    print("读数据",response.service_data.values[event["DID"]]) # This is a dict of DID:Value
                    if not self.DiagDisplayRespResult(response, "进安全模式"):
                        return
                elif diag_cmd == "读故障码":
                    response = uds_client.get_dtc_by_status_mask(9)
                    if not self.DiagDisplayRespResult(response, "读故障码"): return
                    err_code_list = response.service_data.dtcs
                    print('Dbg:"EcuDiag.py" :194 err_code_list=', err_code_list)
                    for err_code in err_code_list:
                        _str = hex(err_code.id)
                        if _str in list(self.Ecu_config_dict["故障"]["故障码列表"].keys()):
                            print(
                                'Dbg:"EcuDiag.py" :198  故障码列表=',
                                self.Ecu_config_dict["故障"]["故障码列表"][str(_str).lower()],
                            )
                        else:
                            print("未知故障", _str)
                        pass
                elif diag_cmd == "清故障码":
                    response = uds_client.clear_dtc()
                    print('Dbg:"EcuDiag.py" :254 清故障response=',response)
                    if not self.DiagDisplayRespResult(response, "清故障失败"): return
                    pass
                elif diag_cmd == "routine":
                    """
                    参数0为16为16进制字符串 ID
                    """
                    routine_id = event["ID"]
                    # routine_data = event["参数"]
                    routine_data = event["参数"]
                    print('Dbg:"EcuDiag.py" :269 routine_id=',hex(routine_id),' ',routine_data)
                    response = uds_client.start_routine(
                        routine_id, data=routine_data#routine 要求参数都是16进制字符串， 方便JSON配置
                    )
                    print('Dbg:"EcuDiag.py" :209 response=', response)
                    if not self.DiagDisplayRespResult(response, "routine 失败"): return
                elif diag_cmd == "控制DTC": 
                    response = uds_client.control_dtc_setting(event["DTC组"])
                    logging.debug('Dbg:"EcuDiag.py" :214 response=', response)
                    if not self.DiagDisplayRespResult(response, " 失败"): return
                elif diag_cmd == "控制通信":
                    response = uds_client.communication_control(
                        int(event["控制模式"]), int(event["通信模式"])
                    )
                    print('Dbg:"EcuDiag.py" :219 response=', response)
                    if not self.DiagDisplayRespResult(response, "  失败"): return
                    pass
                elif diag_cmd == "请求下载":
                    logging.info("开始下载地址数据:"+str([event["地址"],':', event["长度"]]))
                    response = uds_client.request_download(
                        udsoncan.MemoryLocation(
                            event["地址"],
                            memorysize=(event["长度"]),
                            address_format=32,
                            memorysize_format=32,
                        )
                    )
                    RequestDownload.interpret_response(response)
                    # 少发了2个字节, 为服务号和字节长度, 或许。。也许是，记不清了
                    self.Transfer_data_max_len = response.service_data.max_length - 2
                    print(
                        'Dbg:"EcuDiag.py" :239 Transfer_data_max_len=',
                        self.Transfer_data_max_len,
                    )
                    if not self.DiagDisplayRespResult(response, " 失败"): return
                elif diag_cmd == "传输数据":
                    start = 0
                    end = 0
                    data = event["数据"]

                    sequence_number = 0
                    while end < len(data):
                        start = sequence_number * self.Transfer_data_max_len
                        end = min( len(data), (sequence_number + 1) * self.Transfer_data_max_len )
                        tmp_data = bytes(data[start:end])
                        response = uds_client.transfer_data(
                            (sequence_number + 1) & 0xFF, data=tmp_data
                        )
                        if not self.DiagDisplayRespResult(response, "传输数据失败"): return False
                        TransferData.interpret_response(response)
                        seq_number = response.service_data.sequence_number_echo
                        logging.info('seq_number=' +str(seq_number)+"总计："+str((len(data)//self.Transfer_data_max_len) +1))
                        sequence_number += 1
                    uds_client.request_transfer_exit()  # self.DiagComplexEvent(event_list)
                    pass
                else:
                    s = "未知的命令"
                    print('Dbg:"EcuDiag.py" :254 s=', s,diag_cmd)
                pass  # elif
                if event_delay > 0:
                    time.sleep(event_delay)
                pass  # end for
            except Exception as e:
                logging.info("诊断出现错误"+str(e))
                # print 'str(e):\t\t', str(e)
                # print 'repr(e):\t', repr(e)
                # print 'e.message:\t', e.message
                # print 'traceback.print_exc():'; traceback.print_exc()
                print (traceback.format_exc())
                # uds_client.close()
                return False
        uds_client.close()
        return True


    def UpdateConfigDir(self,parent_dict,parent_dir,index):
        '''
            更新文件夹，将子文件夹作为值， 文件夹做为键
            第四层文件夹时读取json配置文件
        '''
        # print('Dbg:"EcuDiag.py" :414 parent_dir=',parent_dir)
        deep_name_list =  ["company_dict","vehicle_dict","ecu_dict"]
        for _root,_dir,_file in 

        dir_array = UtilStr().GetDirFile(parent_dir)["dir_array"]
        index += 1
        if(index == 4):
        # if(index == 2):
            #第三级开始读取File
            ecu_file_array = UtilStr().GetDirFile(parent_dir)["file_array"]
            for temp_file in ecu_file_array:
                tmp_config_file = parent_dir + "/" + temp_file
                if temp_file.lower().endswith("diagconfig.json"):
                    logging.info("加载"+str(tmp_config_file))
                    parent_dict = self.LoadConfig(config_file = tmp_config_file)
                pass
            if(not parent_dict):
                #搜索父目录的配置文件，直到顶层
                info="没有有效的诊断配置文件"
                logging.debug('Dbg:"EcuDiag.py" :377 info=parent_dir'+str(info)+" 诊断文件名为 XXXdiagconfig.json xxx为控制器名 ")

        for tmp_dir in dir_array:
            next_dir = parent_dir + "/" + tmp_dir
            if(index)<=len(deep_name_list):
                next_dict = self.UpdateConfigDir({},next_dir,index) 
                parent_dict[tmp_dir] = next_dict
            # else:
                pass
        return parent_dict
    def LoadConfig(self, config_file):
        '''
            查找配置文件，如果找不到则对上层搜索， 一共搜索4层
        '''

        # 加载某个文件配置
        # self.Config_rout_file_array.insert(0,config_file)
        info = "获取诊断配置信息"
        logging.info( info+str(config_file))
        # logging.info(config_file)
        with open(config_file, "r", encoding="utf8") as config_file_handle:
            diag_config = json.load(config_file_handle)

        if diag_config == None:
            print(config_file, "诊断配置文件有问题")
            return None
        else:
            if diag_config["参考文件"] != "None":
                tmp = (
                    os.path.dirname(os.path.realpath(config_file))
                    + "/"
                    + diag_config["参考文件"]
                )
                with open(tmp, "r", encoding="utf8") as config_file_handle:
                    ref_diag_config = json.load(config_file_handle)
                for tmp_key in ref_diag_config:
                    # print('Dbg:"EcuDiag.py" :412 tmp_key=',tmp_key)
                    if tmp_key not in diag_config:
                        info = "从关联文件加载配置"
                        diag_config[tmp_key] = ref_diag_config[tmp_key]
                        pass
            else:
                # 没有参考文件
                pass
        pass
        return diag_config


    def DiagDisplayRespResult(self, response, msg):
        res = True
        tmp = response.positive
        if not tmp:
            print('Dbg:"EcuDiag.py" :361 tmp=', tmp)
            if("payload" in dir(response)): print(msg, "失败", response.payload)
            res = False
        return res

    def GetCompany(self):
        # 获取所有的公司列表
        company_name_array = list(self.Config_dict.keys())
        print('Dbg:"EcuDiag.py" :488 company_name_array=',company_name_array)
        pass
        return company_name_array

    def GetVehicle(self, company_name):
        vehicle_array = []
        for company in self.Config_dict.keys():
            print('Dbg:"EcuDiag.py" :375 company=', company)
            if company == company_name:
                vehicle_array = list(self.Config_dict[company].keys())
        return vehicle_array  # <<end GetVehicle

    def GetEcu(self, ecu_select_path_list):
        print('Dbg:"EcuDiag.py" :493 ecu_select_path_list=',ecu_select_path_list)
        ecu_list = list(
            self.Config_dict[ecu_select_path_list[0]][
                ecu_select_path_list[1]
            ].keys()
        )
        return ecu_list  # <<end GetVehicle

    def GetEcuConfig(self, ecu_select_path_list=[]):
        """
        获取指定ECU 的配置
            该函数不建议使用
        最后用SetEcu 然后获取self.Ecu_config_dict

        如果path 是None 则返回系统配置过的Ecu
        """
        ecu_config_dict = {}
        if ecu_select_path_list:
            ecu_config_dict = self.Config_dict[ecu_select_path_list[0]][ ecu_select_path_list[1] ][ecu_select_path_list[2]]
        else:
            if self.Ecu_config_dict:
                ecu_config_dict = self.Ecu_config_dict
            else:

                print('Dbg:"EcuDiag.py" :394 info=', info)
        return ecu_config_dict

    def Flash(self, boot_file=None, app_file=None, flash_config_file=None):
        """
        参照flash_config_file 刷写
        """
        # boot_file="d:/gcbb/python/can/gpythoncan/EcuConfig/Leap/T03/Esc/vcf_mot_c55_v.1.8_32.elf.s19"
        # app_file="d:/gcbb/python/can/gpythoncan/EcuConfig/Leap/T03/Esc/LECRAB00019.s19"
        # flash_config_file = "d:/gcbb/python/can/gpythoncan/EcuConfig/Leap/T03/Esc/Esc_falsh_config.json"
        # Test( "d:/gcbb/python/can/gpythoncan/EcuConfig/Leap/T03/Esc/Esc_falsh_config.json")
        tmp_class = EcuFlashFile(
            boot_file=boot_file, app_file=app_file, flash_cfg_file=flash_config_file
        )

        print('Dbg:"EcuDiag.py" :419 tmp_class=', tmp_class)
        # TODO 先刷Boot 在刷App
        has_boot = False


        event_list = []
        for d in tmp_class.Boot_data:
            event_list.extend(self.FlashBlockData(d))#TODO 需要恢复
            has_boot = True

        if has_boot:
            boot_start_address = bytes( tmp_class.Boot_data[0]["StartAddress"].to_bytes(4, byteorder="big"))
            print('Dbg:"EcuDiag.py" :395 boot_start_address=', boot_start_address)
            print('Dbg:"EcuDiag.py" :430 has_boot=', has_boot)
            # 激活下载·
            # 31 01 02 02 addr3 addr2 addr1 addr0
            event = { "命令": "routine", 
                    "ID": 0x202,
                    "参数": boot_start_address, "延时": 0, "info": "激活操作", }
            event_list.append(event)
            pass
        else:
            print("没有BOOT 数据")

        # 下载APP
        print('Dbg:"EcuDiag.py" :515 tmp_class=',tmp_class)

        ecu_flash_config = tmp_class.Ecu_flash_config
        self.EraseAllType = False #分段擦除
        if ("EraseAllType" in ecu_flash_config) and (ecu_flash_config["EraseAllType"] == 1):
            self.EraseAllType = True 

        if (self.EraseAllType):#先统一擦除
            event = { "命令": "routine", "ID": 0xFF00, "参数": None, "延时": 0, "info": "擦除操作", }
            event_list.append(event)
        crc_res = 0
        for d in tmp_class.App_data:
            event_list.extend(self.FlashBlockData(d))#TODO 需要恢复
            print(
                'Dbg:"EcuDiag.py" :441  d=',
                d["StartAddress"],
                ":",
                d["EndAddress"],
                ":",
                d["WriteNeedEraser"],
            )
            if (self.EraseAllType):
                crc_res = crc32(bytes(d["Data"]))#hex， 一次擦除的分段校验。 事实上只有一个数据段
                print('Dbg:"EcuDiag.py" :528 crc_res=',crc_res)
                event = {"命令": "routine", "ID":0xFF01, "参数": (crc_res).to_bytes(4,'big'), "延时": 0, "info": "APP下载完成后分段校验"}
                event_list.append(event)

        if not(self.EraseAllType):
            event = {"命令": "routine", "ID":0xFF01, "参数":None, "延时": 0, "info": "APP下载完成校验"}
            event_list.append(event)

        evt_str = ["["+evt["命令"]+":"+(evt["info"] if ("info" in evt.keys()) else "无信息" )+"]," for evt in event_list]
        logging.info('evt_str='+str(evt_str))
        self.Diag_event_quee.put(event_list)

    # def TransferData(self,data)
    def FlashBlockData(self, block_data):
        """
        下载连续的数据块
        block_data = {
            "WriteNeedEraser":1
            "StartAddress"
            "EndAddress"
            "Data"
        }
        """
        logging.debug('block_data='+ str(block_data["StartAddress"]))
        logging.debug('block_data_erase=', block_data["WriteNeedEraser"])
        start_address = block_data["StartAddress"]
        memeory_len = block_data["EndAddress"] - block_data["StartAddress"]
        start_address_bytes = start_address.to_bytes(4, byteorder="big")
        memeory_len_bytes = memeory_len.to_bytes(4, byteorder="big")
        event_list = []
        if (self.EraseAllType == 0) and (block_data["WriteNeedEraser"] == 1):
            event_list.append(
                {
                    "命令": "routine",
                    "ID":Routine.EraseMemory,
                    "参数": start_address_bytes + memeory_len_bytes,
                    "延时": 0,
                    "info": "块写入前擦除",
                }
            )

        event = {"命令": "请求下载", 
                "地址":start_address,
                "长度": memeory_len, "延时": 0}
        event_list.append(event)

        # 依据回告字节长度传输数据
        event = {"命令": "传输数据", "数据": block_data["Data"], "延时": 0}
        event_list.append(event)
        # event = { "命令":"退出传输数据", "参数":[], "延时":0 }
        # print('Dbg:"EcuDiag.py" :368 event_list=',event_list)
        # self.DiagComplexEvent(event_list)
        # self.Diag_event_quee.put(event_list)
        # 分段传输数据
        return event_list


class Test:
    def __init__(self):
        print("测试")
        pass


if __name__ == "__main__":
    # Test("d:/gcbb/python/can/gpythoncan/EcuConfig/Leap/T03/Esc/Ledbaa00183.s19")
    Test()
